import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.Timer;
import java.io.FileInputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;

public class ServerThread extends Thread{

	private Socket clientSocket;
	private DataOutputStream outToClient;
	private BufferedReader inFromClient;
	private Database data;
	private String clientSentence;
	private User thisUser=null;
	private BSSInterface BSS;
	private ASInterface AS;
	private EIDCreator eidc;
	private Event ev;
	private String keyDirPath;
	private String userName="";
	private String respondMessage="";
	private int clientPort;
	private String clientIP;

	//Hmac, b64 etc.
	private Key secretKey;
	private byte[] keyBytes;
	private String pathToSecretKey;
	private FileInputStream fis;
	private byte[] input;
	private Mac hMac;
	private byte[] encryptedMessage;
	private byte[] base64Message;

	//Handshake
	private String serverPrivKeyPath;
	private boolean authSucc = false;
	private byte[] serverChallenge;
	private boolean fileErr = false;
	private SecretKey aesKey = null;
	private IvParameterSpec iVector; 
	private Server server = null;
	/*TODO exit Befehl wieder zugaenglich machen bei Server*/


	public ServerThread(Socket s, Database data, BSSInterface BSS, ASInterface AS, EIDCreator eidc, String serverPrivKeyPath, String keyDirPath, Server server){
		this.clientSocket=s;
		this.data=data;
		this.BSS=BSS;
		this.AS=AS;
		this.eidc=eidc;
		this.keyDirPath=keyDirPath;
		this.serverPrivKeyPath=serverPrivKeyPath;
		this.server = server;
	}

	public void run(){
		try{
			//Create Input stream, attached to Socket
			inFromClient= new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

			//Create Output stream, attached to Socket
			outToClient= new DataOutputStream(clientSocket.getOutputStream());

			while(true){

				//Read in line from socket
				clientSentence = inFromClient.readLine();

				if(thisUser == null && clientSentence.equals("!list")){

					outToClient.writeBytes(data.getAllAuctions()+'\n');

				}else if(thisUser == null && clientSentence.equals("!end")){
					break;

				}else if(thisUser == null){
					////////////////////////////////////////////////////////Handshake////////////////////////////////////////////////////////

					//Initialising Handshake with decoding of Phase1-Message

					//Reading Server Private Key
					PrivateKey privateKey = getPrivateKey(serverPrivKeyPath);
					if(fileErr){
						outToClient.writeBytes("ERROR:No KeyPair Found");
						authSucc = false;
						fileErr = false;
					}else{
						//RSA-decryption+b64 decoding
						clientSentence = rsaB64Cipher("DECRYPT_MODE", privateKey, clientSentence);

						//Falls korrekter Befehl wird Handshake fortgesetzt
						if(clientSentence.length()>=6 && clientSentence.substring(0,6).equals("!login")){					
							int nextEnd = getNextSpace(7,clientSentence.length());

							//Username, ClientTCP-Port und Client-Challenge werden ausgelesen
							userName = clientSentence.substring(7, nextEnd);
							String clientChallenge = clientSentence.substring(getNextSpace(nextEnd+1,clientSentence.length())+1,clientSentence.length());

							clientPort = Integer.valueOf(clientSentence.substring(nextEnd+1, getNextSpace(nextEnd+1,clientSentence.length())));
							clientIP=clientSocket.getInetAddress().toString();

							//Veraendert den User der List der onlineClients ind Database (ohne User, da logged out)
							data.editOnlineUser(clientIP.substring(1,clientIP.length())+":"+clientPort, userName);

							//Secret Key wir ausgelesen
							keyBytes = new byte[1024];
							pathToSecretKey = keyDirPath + userName+ ".key";
							fis = new FileInputStream(pathToSecretKey);
							fis.read(keyBytes);
							fis.close();
							input = Hex.decode(keyBytes);
							secretKey = new SecretKeySpec(input, "HmacSHA256");
							//erstellt hmac mit dem ausgelesenen Key
							hMac = Mac.getInstance("HmacSHA256"); 
							hMac.init(secretKey);

							//!!!!!!!!!!!!Handshake geht weiter mit mehreren moeglichen Stellen zum Senden der !ok-Message!!!!!!!!!!!!

							//sieht nach ob der User existiert
							if(data.checkUser(userName)==true){

								//ueberprueft ob der User schon online ist
								if(!data.getUser(userName).isOnline()){

									//Handshake-Stelle fuer Message2
									//generierte !ok-message wird mit rsapubKey verschluesselt
									PublicKey pk = getPublicKey(keyDirPath+userName+".pub.pem");
									if(fileErr){
										outToClient.writeBytes("ERROR:No KeyPair Found");
										authSucc = false;
										fileErr = false;
									}else{
										outToClient.writeBytes(rsaB64Cipher("ENCRYPT_MODE", pk, generateOk(clientChallenge))+'\n');
										//outToClient.writeBytes(generateOk(clientChallenge)+'\n');
										authSucc = true;
										clientSentence = inFromClient.readLine();
										//String cSChallenge = clientSentence;
										String cSChallenge = aesB64SCypher("DECRYPT_MODE", aesKey, iVector, clientSentence);
										//System.out.println(cSChallenge);
										byte[] cServerChallenge = Base64.decode(cSChallenge);
										/*System.out.println(serverChallenge);
									System.out.println(cServerChallenge);
									if(MessageDigest.isEqual(serverChallenge, cServerChallenge)){

										System.out.println("++");
									}else System.out.println("--");*/

									}
									if(authSucc){
										outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,"USER "+userName)+ '\n');

										respondMessage="Sucessfully logged in as " + userName + ".";
										hMac.update(respondMessage.getBytes());
										encryptedMessage = hMac.doFinal();
										base64Message = Base64.encode(encryptedMessage);

										outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');

										/*try{
										if(AS!=null){
											//USER_LOGIN-Event wird an AS geschickt 
											ev = new UserEvent(eidc.getACID(), "USER_LOGIN",System.currentTimeMillis(),userName);
											AS.processEvent(ev);
										}else System.out.println("WARNING: AnalyticsServer is not available");
									}catch(RemoteException e1) {
										//e1.printStackTrace();
										AS=null;
										System.out.println("WARNING: AnalyticsServer is not available");
									}*/
									}else{
										//Authentifizierung fehlgeschlagen -> userName wird wieder zurueckgesetzt
										userName="";
										authSucc = false;
									}
								}
								else{
									userName="";
									outToClient.writeBytes("ERROR: This User is already logged in!"+'\n');
								}
							}
							else{
								//Erzeugt neuen User falls es noch keinen mit solchem Namen gegeben hat.
								//Handshake-Stelle fuer Message2
								//generierte !ok-message wird mit rsapubKey verschluesselt
								PublicKey pk = getPublicKey("keys/"+userName+".pub.pem");
								if(fileErr){
									outToClient.writeBytes("ERROR:Userfile existiert nicht");
									authSucc = false;
									fileErr = false;
								}else{
									outToClient.writeBytes(rsaB64Cipher("ENCRYPT_MODE", pk, generateOk(clientChallenge))+'\n');
									//outToClient.writeBytes(generateOk(clientChallenge)+'\n');
									authSucc = true;
									clientSentence = inFromClient.readLine();
									//String cSChallenge = clientSentence;
									String cSChallenge = aesB64SCypher("DECRYPT_MODE", aesKey, iVector, clientSentence);
									//System.out.println(cSChallenge);
									byte[] cServerChallenge = Base64.decode(cSChallenge);

									/*System.out.println(serverChallenge);
								System.out.println(cServerChallenge);
								if(MessageDigest.isEqual(serverChallenge, cServerChallenge)){

									System.out.println("++");
								}else System.out.println("--");*/

								}
								if(authSucc){

									//outToClient.writeBytes("USER "+userName+ '\n');
									outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,"USER "+userName)+ '\n');
									respondMessage="New User named "+ userName + " has been created.";
									hMac.update(respondMessage.getBytes());
									encryptedMessage = hMac.doFinal();
									base64Message = Base64.encode(encryptedMessage);
									outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');

									/*try{
									if(AS!=null){
										//USER_LOGIN-Event wird an AS geschickt 
										ev = new UserEvent(eidc.getACID(), "USER_LOGIN",System.currentTimeMillis(),userName);
										AS.processEvent(ev);
									}else System.out.println("WARNING: AnalyticsServer is not available");
								}catch(RemoteException e1) {
									//e1.printStackTrace();
									AS=null;
									System.out.println("WARNING: AnalyticsServer is not available");
								}*/
								}else{
									//Authentifizierung fehlgeschlagen -> userName wird wieder zurueckgesetzt
									userName="";
								}
							}
							//Wenn der User noch nicht online ist, wird der User nun auf online gesetzt
							if(!userName.equals("")){
								thisUser=data.getUser(userName);
								thisUser.setOnline();
							}				
						}
					}
				}else{
					////////////////////////////////////////////////////////Handshake war erfolgreich AES-Entschluesselung plus eingelogte Befehle verfuegbar////////////////////////////////////////////////////////	
					//AES-Entschluesselung:
					clientSentence = aesB64SCypher("DECRYPT_MODE", aesKey, iVector,clientSentence);
					//System.out.println(clientSentence);
					if(clientSentence.length()>=4 && clientSentence.substring(0,4).equals("!end")){
						/*try{
							if(AS!=null){
								//USER_LOGOUT-Event wird an AS geschickt 
								ev = new UserEvent(eidc.getACID(), "USER_LOGOUT",System.currentTimeMillis(),thisUser.getName());
								AS.processEvent(ev);
							}else System.out.println("WARNING: AnalyticsServer is not available");
						}catch(RemoteException e1) {
							//e1.printStackTrace();
							AS=null;
							System.out.println("WARNING: AnalyticsServer is not available");
						}*/
						break;
					}
					//!logout Befehl wird verarbeitet
					else if(clientSentence.equals("!logout")){
						/*try{
							if(AS!=null){
								//USER_LOGOUT-Event wird an AS geschickt 
								ev = new UserEvent(eidc.getACID(), "USER_LOGOUT",System.currentTimeMillis(),thisUser.getName());
								AS.processEvent(ev);
							}else System.out.println("WARNING: AnalyticsServer is not available");
						}catch(RemoteException e1) {
							//e1.printStackTrace();
							AS=null;
							System.out.println("WARNING: AnalyticsServer is not available");
						}*/

						//Veraendert den User der List der onlineClients ind Database (ohne User, da logged out)
						data.editOnlineUser(clientIP.substring(1,clientIP.length())+":"+clientPort, "not logged in");

						respondMessage="Sucessfully logged out as " + thisUser.getName()+"!";
						hMac.update(respondMessage.getBytes());
						encryptedMessage = hMac.doFinal();
						base64Message = Base64.encode(encryptedMessage);
						//outToClient.writeBytes(respondMessage +new String(base64Message)+ '\n');
						outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');
						//outToClient.writeBytes("USER"+ '\n');
						outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,"USER")+ '\n');
						outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,"STOP") + '\n');
						thisUser.setOffline();
						thisUser=null;
						authSucc=false;


					}
					//bei einem uebertragungsfehler wird der letzte Befehl nochmal gesendet
					else if(clientSentence.equals("!sendAgain")){
						//outToClient.writeBytes(respondMessage +new String(base64Message)+ '\n');
						outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');
					}
					//!login Befehl wird verarbeitet
					else if(clientSentence.length()>=6 && clientSentence.substring(0,6).equals("!login")){					

						respondMessage="Your are already logged in as another user!";
						hMac.update(respondMessage.getBytes());
						encryptedMessage = hMac.doFinal();
						base64Message = Base64.encode(encryptedMessage);

						//Veraendert den User der List der onlineClients ind Database (ohne User, da logged out)
						data.editOnlineUser(clientIP.substring(1,clientIP.length())+":"+clientPort, thisUser.getName());

						//outToClient.writeBytes(respondMessage +new String(base64Message)+ '\n');
						outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');

					}
					//!list Befehl wird verarbeitet
					else if(clientSentence.equals("!list")){

						respondMessage=data.getAllAuctions();
						hMac.update(respondMessage.getBytes());
						encryptedMessage = hMac.doFinal();
						base64Message = Base64.encode(encryptedMessage);

						//outToClient.writeBytes(respondMessage +new String(base64Message)+ '\n');
						outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');


					}
					//gibt die IPs, Ports sowie Name aller momentan sich online befindlichen Clients zurueck
					else if(clientSentence.length()>=14 && clientSentence.substring(0,14).equals("!getClientList")){

						respondMessage=data.getOnlineUserList();
						hMac.update(respondMessage.getBytes());
						encryptedMessage = hMac.doFinal();
						base64Message = Base64.encode(encryptedMessage);
						outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');


					}
					//!create Befehl wird verarbeitet
					else if(clientSentence.length()>=11 && clientSentence.substring(0,7).equals("!create")){
						try{
							int space= getNextSpace(8,clientSentence.length());

							String sTime=clientSentence.substring(8, space);

							String name= clientSentence.substring(space+1, clientSentence.length());

							//String wird in einen int umgewandelt
							int time= Integer.parseInt(sTime);

							String endTime= getEndTime(time);

							//Neue Auktion wird erstellt...
							Auction a=new Auction(name, thisUser, endTime, this.data, this.BSS, this.AS, this.eidc);
							//... und der Database hinzugefuegt.
							data.addAuctionToList(a);

							//danach wird time (in Sekunden gewartet), woraufhin in Auction run() ausgefuehrt wird.
							Timer t=new Timer();
							t.schedule(a, time*1000);
							a.addTimer(t);

							respondMessage="An auction '"+name+"' with id "+ data.getAuction(name) +" has been created and will end on "+endTime+".";
							hMac.update(respondMessage.getBytes());
							encryptedMessage = hMac.doFinal();
							base64Message = Base64.encode(encryptedMessage);

							//outToClient.writeBytes(respondMessage +new String(base64Message)+ '\n');
							outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');

						}catch(NumberFormatException nfe){
							respondMessage= "Wrong !create comment, USAGE: <!create> <auctionTime> <bauctionName>";
							hMac.update(respondMessage.getBytes());
							encryptedMessage = hMac.doFinal();
							base64Message = Base64.encode(encryptedMessage);
							//outToClient.writeBytes(respondMessage +new String(base64Message)+ '\n');
							outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');

						}
						/*	try{
							if(AS!=null){
								//AUCTION_STARTED-Event wird an AS geschickt 
								ev = new AuctionEvent(eidc.getACID(), "AUCTION_STARTED",System.currentTimeMillis(),a.getAID());
								AS.processEvent(ev);
							}else System.out.println("WARNING: AnalyticsServer is not available");
						}catch(RemoteException e1) {
							//e1.printStackTrace();
							AS=null;
							System.out.println("WARNING: AnalyticsServer is not available");
						}*/
					}

					//!bid Befehl wird verarbeitet.
					else if(clientSentence.length()>=4 && clientSentence.substring(0,4).equals("!bid")){

						try{
							int space= getNextSpace(5,clientSentence.length());
							//liest die ID der auf zu bietenden Auktion aus
							int id=Integer.parseInt(clientSentence.substring(5, space));

							//liest den zu bietenden Betrag auf die Auktion aus
							double bid=Double.parseDouble(clientSentence.substring(space+1, clientSentence.length()));

							respondMessage=data.bidOnAuction(id, bid, thisUser);
							hMac.update(respondMessage.getBytes());
							encryptedMessage = hMac.doFinal();
							base64Message = Base64.encode(encryptedMessage);
							//outToClient.writeBytes(respondMessage +new String(base64Message)+ '\n');
							outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');

						}catch(NumberFormatException nfe){
							respondMessage= "Wrong !bid comment, USAGE: <!bid> <auctionID> <bidPrice>";
							hMac.update(respondMessage.getBytes());
							encryptedMessage = hMac.doFinal();
							base64Message = Base64.encode(encryptedMessage);
							//outToClient.writeBytes(respondMessage +new String(base64Message)+ '\n');
							outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');

						}

					}else{

						respondMessage = "Unknown command.";
						hMac.update(respondMessage.getBytes());
						encryptedMessage = hMac.doFinal();
						base64Message = Base64.encode(encryptedMessage);
						//outToClient.writeBytes(respondMessage +new String(base64Message)+ '\n');
						outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,(respondMessage +new String(base64Message)))+ '\n');

					}
				}	
				if(authSucc){
					//sendet die aktuelle onlineClientList an den Client
					outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,"onlineClientList"+data.getOnlineUserList()) + '\n');
					outToClient.writeBytes(aesB64SCypher("ENCRYPT_MODE", aesKey, iVector,"STOP") + '\n');

				}
			}
			clientSocket.close();
			inFromClient.close();
			outToClient.close();

		}catch(IOException e){

			/*if(thisUser != null){
				try {
					if(AS!=null){
						//USER_DISCONNECTED-Event wird an AS geschickt 
						ev = new UserEvent(eidc.getACID(), "USER_DISCONNECTED",System.currentTimeMillis(),thisUser.getName());
						AS.processEvent(ev);
					}else System.out.println("WARNING: AnalyticsServer is not available");
				}catch(RemoteException e1) {
					//e1.printStackTrace();
					AS=null;
					System.out.println("WARNING: AnalyticsServer is not available");
				}
			}*/

			//e.printStackTrace();
			System.out.println("ServerThread got shut down.");
		}
		catch(NoSuchAlgorithmException nsae){
			nsae.printStackTrace();
		}
		catch (java.security.InvalidKeyException e) {
			System.err.println("Das jeweilige file beinhaltet einen ungueltigen Key.");
			//e.printStackTrace();
		} 

	}
	////////////////////////////////////////////////////////////Methoden////////////////////////////////////////////////////////////
	/**
	 * @param keyPath
	 * @return Private Key wird aus file ausgelesen.
	 */
	private PrivateKey getPrivateKey(String keyPath){
		KeyPair keyPair = new KeyPair(null, null);
		try{
			PEMReader reader = new PEMReader(new FileReader(keyPath), new PasswordFinder(){
				public char[] getPassword() {

					// reads the password from standard input for decrypting the private key
					//return pwInput.toCharArray();
					//					System.out.println("PW eingegeben!");
					//					return "23456".toCharArray();

					return server.getSysInput().toCharArray();
				}});

			keyPair = (KeyPair) reader.readObject();
			reader.close();
		} catch (FileNotFoundException e) {
			fileErr = true;
			//e.printStackTrace();
		} catch (IOException e) {

			e.printStackTrace();
		}
		return keyPair.getPrivate();
	}
	/**
	 * @param keyPath
	 * @return Public Key wird aus file ausgelesen.
	 */
	private PublicKey getPublicKey(String keyPath){

		PEMReader in = null;
		PublicKey publicKey = null;
		try {
			in = new PEMReader(new FileReader(keyPath));
			publicKey = (PublicKey) in.readObject();
			in.close();
		} catch (FileNotFoundException e) {
			fileErr = true;
			//e.printStackTrace();
		} catch (IOException e) {

			e.printStackTrace();
		}
		return publicKey;
	}
	//RSA
	/**
	 * @param cyphermode
	 * @param key
	 * @param sentence
	 * @return sentence wird je nach Mode mit RSA und Base64 verschluesselt oder entschluesselt
	 */
	private String rsaB64Cipher(String cyphermode, Key key, String sentence){
		try{
			if(cyphermode.equals("ENCRYPT_MODE")){

				Cipher crypt = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding"); 
				crypt.init(Cipher.ENCRYPT_MODE, key);
				byte[] encrypt = crypt.doFinal(sentence.getBytes());
				return new String(Base64.encode(encrypt));	

			}else {
				byte[] dByte = Base64.decode(sentence.getBytes());
				Cipher crypt = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding"); 
				crypt.init(Cipher.DECRYPT_MODE, key);
				byte[] decrypt = crypt.doFinal(dByte);
				return new String(decrypt);	
			}
		}catch (IllegalBlockSizeException e) {
			System.err.println("RSA encryption/decryption error");
			authSucc = false;
			e.printStackTrace();
		} catch (BadPaddingException e) {
			System.err.println("RSA encryption/decryption error");
			authSucc = false;
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			System.err.println("RSA encryption/decryption error");
			authSucc = false;
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			System.err.println("RSA Key error");
			authSucc = false;
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			System.err.println("RSA encryption/decryption error");
			authSucc = false;
			e.printStackTrace();
		}
		return "RSA encryption/decryption error";

	}
	//AES:
	/**
	 * @param cyphermode
	 * @param key
	 * @param iV
	 * @param sentence
	 * @return sentence wird je nach Mode mit AES und Base64 verschluesselt oder entschluesselt
	 */
	private String aesB64SCypher(String cyphermode, Key key, IvParameterSpec iV, String sentence){

		try{
			if(cyphermode.equals("ENCRYPT_MODE")){

				Cipher crypt = Cipher.getInstance("AES/CTR/NoPadding"); 
				crypt.init(Cipher.ENCRYPT_MODE, key, iV);
				byte[] encrypt = crypt.doFinal(sentence.getBytes());
				return new String(Base64.encode(encrypt));	

			}else {
				byte[] dByte = Base64.decode(sentence.getBytes());
				Cipher crypt = Cipher.getInstance("AES/CTR/NoPadding"); 
				crypt.init(Cipher.DECRYPT_MODE, key, iV);
				byte[] decrypt = crypt.doFinal(dByte);
				return new String(decrypt);	
			}
		}catch (IllegalBlockSizeException e) {
			System.err.println("AES encryption/decryption error");
			e.printStackTrace();
		} catch (BadPaddingException e) {
			System.err.println("AES encryption/decryption error");
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			System.err.println("AES encryption/decryption error");
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			System.err.println("AES Key error");
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			System.err.println("AES encryption/decryption error");
			e.printStackTrace();
		} catch (InvalidAlgorithmParameterException e) {
			System.err.println("AES iVParameter error");
			e.printStackTrace();
		}
		return "AES encryption/decryption error";

	}
	/**
	 * @param clientChallenge
	 * @return alle Bestandteile, sowie die !ok-Message selbst werden generiert
	 */
	private String generateOk(String clChallenge){

		String ok= "!ok";

		// Server-Challenge
		SecureRandom secureRandom = new SecureRandom(); 
		final byte[] serverC = new byte[32]; 
		secureRandom.nextBytes(serverC);
		serverChallenge = serverC;
		String sChallenge = new String(Base64.encode(serverC));
		// AES-Key
		KeyGenerator generator = null;
		try {
			generator = KeyGenerator.getInstance("AES");
		} catch (NoSuchAlgorithmException e) {
			System.err.println("Keygenerator Error");
			e.printStackTrace();
		} 
		generator.init(256); 
		aesKey = generator.generateKey(); 
		byte[] keyByte = aesKey.getEncoded();
		//System.out.println(Arrays.toString(keyByte));
		String sAESKey = new String(Base64.encode(keyByte));
		//System.out.println(sAESKey.length());
		//System.out.println(sAESKey);
		// IV - Parameter
		final byte[] iVNumber = new byte[16]; 
		secureRandom.nextBytes(iVNumber);
		byte[] iVB = iVNumber;
		String iVS = new String(Base64.encode(iVB));
		iVector = new IvParameterSpec(iVB);




		ok = new String(ok+" " +clChallenge+" "+sChallenge+" "+sAESKey+" "+iVS+"");  

		return ok;
	}

	//gibt die Stelle im String zurueck, an der sich das naechste Leerzeichen befindet
	private int getNextSpace(int begin, int end){

		for(int n=begin; n<end; n++){
			if(clientSentence.charAt(n)==' '){
				end=n;
				break;
			}
		}
		return end;
	}

	//Hilf die verlangte Abgabe der Zeitanzeige zu erfuellen
	private String getEndTime(int time){

		//wandelt time (in sekunden) in minuten, stunden etc. um
		int sec=time%60;
		time=time/60;
		int min=time%60;
		time=time/60;
		int h=time%24;
		time=time/24;
		int d=time;

		//fuegt die time-Zeit, zur aktuellen Zeit hinzu
		Calendar cal = Calendar.getInstance(); // creates calendar
		cal.setTime(new Date()); //sets calendar time/date
		cal.add(Calendar.SECOND, sec);
		cal.add(Calendar.MINUTE, min);
		cal.add(Calendar.HOUR_OF_DAY, h);
		cal.add(Calendar.DAY_OF_MONTH, d);

		//Diese if-Abfrage ist notwendig, da bei z.B.: 18:05 ansonsten 18:5 ausgegeben wird. Selbiges bei hours.
		String minutes=""+cal.get(Calendar.MINUTE);
		String hours=""+cal.get(Calendar.HOUR_OF_DAY);
		if(cal.get(Calendar.MINUTE)<10){
			minutes= "0"+cal.get(Calendar.MINUTE);
		}
		if(cal.get(Calendar.HOUR_OF_DAY)<10){
			hours= "0"+cal.get(Calendar.HOUR_OF_DAY);
		}

		return ""+cal.get(Calendar.DAY_OF_MONTH) +"."+ (cal.get(Calendar.MONTH)+1)+ "."+cal.get(Calendar.YEAR) + " "+hours+":"+minutes+ " "+cal.getTimeZone().getDisplayName(true, TimeZone.SHORT);
	}

	//Wird verwendet um ServerThread zu beenden
	public void threadShutdown(){
		try{
			clientSocket.close();
		}
		catch(IOException e){
			//e.printStackTrace();
			System.out.println("ServerThread got shut down.");
		}
	}


}
